home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / news / transport / cnews / msgidd / msgidd.shar / msgidd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-26  |  11.2 KB  |  549 lines

  1. /* msgidd -- message ID daemon
  2.  * vix 24may90 [written]
  3.  *
  4.  * with mods ken@sdd.hp.com 01jul90
  5.  * speedups by Geoff Collyer, 26 July 1992
  6.  *
  7.  * $Header: msgidd.c,v 1.8 91/05/21 19:47:48 vixie Locked $
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <signal.h>
  13. #include <errno.h>
  14. #include <syslog.h>
  15. #include <sys/types.h>
  16. #include <sys/socket.h>
  17. #include <sys/un.h>
  18. #include <sys/time.h>
  19. #ifdef hpux
  20. #include <sys/param.h>
  21. #include <libBSD.h>
  22. #endif
  23. #define NEEDMSGS
  24. #include "msgid.h"
  25.  
  26. #define ASSERT(e, m) if (!(e)) {fputs("assert failed... ", stderr);\
  27.                 perror(m); exit(1);}
  28.  
  29. #define STREQ(s1, s2)        (*(s1) == *(s2) && strcmp(s1, s2) == 0)
  30. #define STRN_EQ(s1, s2, n)    (*(s1) == *(s2) && strncmp(s1, s2, n) == 0)
  31.  
  32. #define FLAGS_RESETLOG    0x02
  33. #define FLAGS_FLUSHLOG    0x04
  34. #define MAX_AGE        10
  35. #define ALARM_TIME    300
  36.  
  37. #define HASHSIZE 1024
  38.  
  39. #if 0
  40. #define dprintf fprintf
  41. #else
  42. #define dprintf (void)
  43. #endif
  44.  
  45. char *malloc();
  46. extern int errno;
  47.  
  48. int log = 0, flags = 0;
  49. time_t hold_time = MAX_AGE * 60;
  50. char *hosts[100], *lfn, *ptime();
  51. FILE *logfp = NULL;
  52.  
  53. struct {
  54.     int connected;
  55.     int connects, drops;
  56.     int new, dup, cancel;
  57.     int freed;
  58. } stats;
  59.  
  60. struct el {
  61.     struct el *next;
  62.     time_t age;
  63.     int refcnt;
  64.     char id[1];
  65. } *ids[HASHSIZE];
  66.  
  67. char *months[12] = {
  68.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  69.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  70. };
  71.  
  72. static int transaction();
  73.  
  74. static unsigned
  75. mkhash(s)
  76. register unsigned char *s;
  77. {
  78.     register unsigned hash = 0;
  79.     register unsigned char c;
  80.  
  81.     while ((c = *s++) != '\0')
  82.         hash += c;
  83.     return hash;
  84. }
  85.  
  86. static char *
  87. strdup(x)
  88. char *x;
  89. {
  90.     register char *y = malloc(strlen(x) + 1);
  91.  
  92.     if (y)
  93.         strcpy(y, x);
  94.     return y;
  95. }
  96.  
  97. static void
  98. savepid ()
  99. {
  100.   FILE *pidfp ;
  101.  
  102.   if ((pidfp = fopen (PIDFILE,"w")) == NULL)
  103.     return ;
  104.  
  105.   (void) fprintf (pidfp,"%d\n",getpid()) ;
  106.   (void) fclose (pidfp) ;
  107. }
  108.  
  109. char *
  110. ptime(now)
  111.     time_t now;
  112. {
  113.     static char buf[50];
  114.     struct tm *tmp;
  115.  
  116.     tmp = localtime(&now);
  117.     (void) sprintf(buf, "%s %2d %02d:%02d:%02d", 
  118.            months[tmp->tm_mon], tmp->tm_mday, tmp->tm_hour, 
  119.            tmp->tm_min, tmp->tm_sec);
  120.     return (buf);
  121. }
  122.  
  123. static void 
  124. usage(me) 
  125.     char *me;
  126. {
  127.     (void) fprintf(stderr, "Usage: %s [options]\n", me);
  128.     (void) fprintf(stderr, "Options: -s <unix domain socketname> [%s]\n",
  129.            SOCKNAME);
  130.     (void) fprintf(stderr, "         -l <log file name>\n");
  131.     (void) fprintf(stderr, "         -h <hold time in minutes>\n");
  132.     exit(1);
  133. }
  134.  
  135. static void
  136. openlogfile()
  137. {
  138.     if (logfp)
  139.     (void) fclose(logfp);
  140.     if (log && (logfp = fopen(lfn, "a+")) == NULL) {
  141.     syslog(LOG_ERR, "Unable to open %s: %m", lfn);
  142.     log = 0;
  143.     logfp = NULL;
  144.     }
  145. }
  146.  
  147. static void
  148. bye()
  149. {
  150.     if (log)
  151.     (void) fclose(logfp);
  152.     dprintf(stderr,"Bye !!\n");
  153.     exit(0);
  154. }
  155.  
  156. static void
  157. resetlog() {
  158.     flags |= FLAGS_RESETLOG;
  159. }
  160.  
  161. static void 
  162. pstats() 
  163. {
  164.     char msgbuf[1024];
  165.  
  166.     if (log)
  167.     flags |= FLAGS_FLUSHLOG;
  168.     sprintf(msgbuf, "stats: %d connected, %d connects, %d drops, %d dups, %d new, %d cancel, %d freed\n",
  169.         stats.connected, stats.connects, stats.drops, stats.dup, stats.new,
  170.         stats.cancel, stats.freed);
  171.     dprintf(stderr, "%s\n", msgbuf);
  172.     syslog(LOG_INFO, msgbuf);
  173.     stats.connects = stats.drops = stats.new = stats.cancel = stats.dup =
  174.     stats.freed = 0;
  175.     alarm(ALARM_TIME);
  176. }
  177.  
  178. int onpipe();
  179.  
  180. main(argc, argv)
  181.     int argc;
  182.     char *argv[];
  183. {
  184.     register char *sn = SOCKNAME;
  185.     register int s;
  186.     int highest_fd;
  187.     struct sockaddr_un n, in;
  188.     fd_set clients;
  189.     extern char *optarg;
  190.     extern int optind;
  191.  
  192.     while ((s = getopt(argc, argv, "l:h:s:")) != EOF)
  193.     switch(s) {
  194.         case 'h':
  195.         hold_time = 60 * atoi(optarg);
  196.         if (hold_time <= 0 || hold_time > (24 * 3600))
  197.             usage(argv[0]);    
  198.         break;
  199.         case 's':
  200.         sn = strdup(optarg);
  201.         break;
  202.         case 'l':
  203.         log++;
  204.         lfn = strdup(optarg);
  205.         break;
  206.         default:
  207.         usage(argv[0]);    
  208.         break;
  209.     }
  210.  
  211.     if (optind != argc)
  212.     usage(argv[0]);
  213.     
  214.     if (log) {
  215.     openlogfile();
  216.     if (!log) {
  217.         (void) fprintf(stderr, "%s: Unable to open log file (%s)\n", 
  218.                argv[0], lfn);
  219.         exit(1);
  220.     }
  221.     }
  222.  
  223.     savepid () ;
  224.  
  225. #ifdef LOG_DAEMON
  226.     openlog("msgidd", LOG_PID, LOG_DAEMON);
  227. #else
  228.     openlog("msgidd", LOG_PID);
  229. #endif
  230.  
  231.     s = socket(PF_UNIX, SOCK_STREAM, 0);
  232.     ASSERT(s>=0, "socket");
  233.     highest_fd = s;
  234.  
  235.     n.sun_family = AF_UNIX;
  236.     (void) strcpy(n.sun_path, sn);
  237.  
  238.     (void) unlink(sn);
  239.     ASSERT(0<=bind(s, &n, strlen(n.sun_path) +sizeof n.sun_family),n.sun_path);
  240.  
  241.     FD_ZERO(&clients);
  242.     listen(s, 5);
  243.  
  244.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  245.     signal(SIGHUP, resetlog);
  246.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  247.     signal(SIGINT, bye);
  248.     signal(SIGUSR1, bye);        /* for profiling, etc. */
  249.     signal(SIGALRM, pstats);
  250.     signal(SIGPIPE, onpipe);
  251.     alarm(ALARM_TIME);
  252.  
  253.     for (;;) {
  254.     register int nfound, fd;
  255.     fd_set readfds;
  256.  
  257.     if (flags) {
  258.         if (flags & FLAGS_FLUSHLOG)
  259.         (void) fflush(logfp);
  260.         if (flags & FLAGS_RESETLOG)
  261.         openlogfile();
  262.         flags = 0;
  263.     }
  264.     readfds = clients;    /* we want to select the clients... */
  265.     FD_SET(s, &readfds);    /* ...and the main server socket. */
  266.     nfound = select(highest_fd+1, &readfds, NULL, NULL, NULL);
  267.     if (nfound < 0 && errno == EINTR)
  268.         continue;
  269.     ASSERT(0<=nfound, "select");
  270.     for (fd = 0; fd <= highest_fd; fd++) {
  271.         if (FD_ISSET(fd, &readfds)) {
  272.         if (fd == s) {
  273.             int fromlen = sizeof(in);
  274.  
  275.             if ((fd = accept(s, &in, &fromlen)) == -1) {
  276.             syslog(LOG_ERR, "Accept failed: %m");
  277.             } else {
  278.             FD_SET(fd, &clients);
  279.             if (fd > highest_fd)
  280.                 highest_fd = fd;
  281.             stats.connects++;
  282.             stats.connected++;
  283.             }
  284.         } else if (FD_ISSET(fd, &clients)) {
  285.             if (-1 == transaction(fd)) {
  286.             close(fd);
  287.             FD_CLR(fd, &clients);
  288.             stats.connected--;
  289.             stats.drops++;
  290.             if (hosts[fd]) {
  291.                 if (log) {
  292.                 (void) fprintf(logfp, "%s Disconnect %s\n", 
  293.                     ptime(time((time_t *)0)), hosts[fd]);
  294.                 }
  295.                 dprintf(stderr, "Disconnect(%d/%s)\n",
  296.                 fd, hosts[fd]);
  297.                 free(hosts[fd]);
  298.                 hosts[fd] = NULL;
  299.                 }
  300.             }
  301.         } else {
  302.             dprintf(stderr, "Bad fd %d from select\n", fd);
  303.         }
  304.         }
  305.     }
  306.     }
  307. }
  308.  
  309. int sigpiped;
  310.  
  311. static int
  312. reply(fd, ans)
  313.     register int fd, ans;
  314. {
  315.     int status;
  316.  
  317.     sigpiped = 0;
  318.     while ((status = write(fd, (ans ? "\001" : "\000"), 1)) < 0
  319.     && errno == EINTR && !sigpiped)
  320.     ;
  321.     if (status < 0) {
  322.     if (log) {
  323.         register time_t now = time((long *)0);
  324.  
  325.         fprintf(logfp, "%s write failed (fd %d)\n", ptime(now), fd);
  326.     }
  327.     syslog(LOG_ERR, "write failed, closing connection: %m");
  328.     return -1;
  329.     }
  330.     return (0);
  331. }
  332.  
  333. static void
  334. cancel(fd, bufp, now)
  335.     register int fd;
  336.     register char *bufp;
  337.     time_t now;
  338. {
  339.     register struct el *i, *p;
  340.     register int found = 0, hash = mkhash(bufp) % HASHSIZE;
  341.  
  342.     for (i = ids[hash], p = NULL; i; p = i, i = i->next)
  343.     if (STREQ(i->id, bufp)) {
  344.         if (p == NULL)
  345.         ids[hash] = i->next;
  346.         else
  347.         p->next = i->next;
  348.         if (log)
  349.         (void) fprintf(logfp, "%s Cancel %s %s\n", 
  350.                    ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
  351.         free(i);
  352.         found++;
  353.         stats.cancel++;
  354.         dprintf(stderr, "Cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  355.         break;
  356.     }
  357.     if (!found) {
  358.     dprintf(stderr, "Bad-cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  359.     syslog(LOG_ERR, "Cancel, %s not found", bufp);
  360.     if (log)
  361.         (void) fprintf(logfp, "%s Error cancel %s %s\n", 
  362.                ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
  363.     }
  364. }
  365.  
  366. static void
  367. add(fd, bufp, n, now)
  368.     register int fd;
  369.     register char *bufp;
  370.     register int n;
  371.     time_t now;
  372. {
  373.     register struct el *i;
  374.     register int hash = mkhash(bufp) % HASHSIZE;
  375.  
  376.     /* this malloc includes the id[1] array, which means
  377.      * that there's already room for strcpy's null
  378.      */
  379.     i = (struct el *) malloc(sizeof(struct el) + n);
  380.     if (i == NULL)
  381.     return;
  382.     i->age = now;
  383.     (void) strcpy(i->id, bufp);
  384.     if (log) {
  385.     i->refcnt = 1;
  386.     (void) fprintf(logfp, "%s Add %s %s\n", ptime(now),
  387.                 hosts[fd] ? hosts[fd] : "", bufp);
  388.     }
  389.     i->next = ids[hash];
  390.     ids[hash] = i;
  391.     stats.new++;
  392.     dprintf(stderr, "Add(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  393. }
  394.  
  395. static int
  396. search(fd, bufp, now)
  397.     register int fd;
  398.     char *bufp;            /* no leading '<' */
  399.     time_t now;
  400. {
  401.     register struct el *i, *p;
  402.     register char bufc = *bufp;
  403.     register int hash = mkhash(bufp) % HASHSIZE;
  404.     int found = 0, searched = 0;
  405.  
  406.     /* 
  407.      * search the appropriate list.
  408.      */
  409.     for (i = ids[hash], p = NULL; i; p = i, i = i->next) {
  410.     /*
  411.      * if too old, everything from here to the end
  412.      * can be nuked (we always add at the top).
  413.      */
  414.     if ((now - i->age) > hold_time) {
  415.         while (i) {
  416.         register struct el *n = i->next;
  417.  
  418.         if (p)
  419.             p->next = n;
  420.         else
  421.             ids[hash] = n;
  422.         free((char *)i);
  423.         i = n;
  424.         stats.freed++;
  425.         }
  426.         break;
  427.     }
  428.  
  429.     searched++;
  430.  
  431.     if (STREQ(i->id, bufp)) {
  432.         if (log) {
  433.         i->refcnt++;
  434.         (void) fprintf(logfp, "%s Lose %s %d %ld %s\n", 
  435.                ptime(now), hosts[fd] ? hosts[fd] : "", i->refcnt, 
  436.                    (now - i->age), i->id);
  437.         }
  438.         found++;
  439.         stats.dup++;
  440.         break;
  441.     }
  442.     }
  443.  
  444.     dprintf(stderr, "Search(%d/%s): %s(%d) `%s'\n",
  445.         fd, hosts[fd], (found ? "dup" : "new"), searched, bufp);
  446.     return (found);
  447. }
  448.  
  449. /* returns: -1 == client is gone, close this fd please
  450.  *           0 == success
  451.  */
  452. static int
  453. transaction(fd)
  454.     register fd;
  455. {
  456.     char buf[1023];
  457.     register int n;
  458.     register char *bufp, *cmdp;
  459.     register time_t now = time((long *)0);
  460.  
  461.     /* read the request.  zero-length read means connection is gone.
  462.      */
  463.     do {
  464.     n = read(fd, buf, sizeof(buf));
  465.     if (n == 0)
  466.         return -1;
  467.     } while (n < 0 && errno == EINTR);
  468.     ASSERT(n>0, "read");
  469.  
  470.     if (hosts[fd]) {
  471.     dprintf(stderr, "Parse(%d/%s): `%s'\n", fd, hosts[fd], buf);
  472.     }
  473.  
  474.     /* Separate cmd from id 
  475.      */
  476.     cmdp = buf;
  477.     bufp = buf + 4;
  478.     n -= 4;
  479.  
  480.     /* find the first useful character, saving it and its address.
  481.      */
  482.     if (*bufp == '<') {
  483.     bufp++;
  484.     n--;
  485.     }
  486.  
  487.     /* rip out useless characters at end, remembering real length.
  488.      */
  489.     while (n > 0) {
  490.     register x = n - 1;
  491.     register ch = bufp[x];
  492.  
  493.     if (ch == '\n' || ch == '\r' || ch == '>')
  494.         n = x;
  495.     else
  496.         break;
  497.     }
  498.     bufp[n] = '\0';
  499.  
  500.     /* 
  501.      * Which cmd ?
  502.      */
  503.     if (STRN_EQ(cmdp, msgs[MCANCEL], 4)) {
  504.     cancel(fd, bufp, now);
  505.     return reply(fd, 0);
  506.     } else if (STRN_EQ(cmdp, msgs[MADD], 4)) {
  507.     if (search(fd, bufp, now))
  508.         return reply(fd, 1);
  509.     else { 
  510.         add(fd, bufp, n, now);
  511.         return reply(fd, 0);
  512.     }
  513.     } else if (STRN_EQ(cmdp, msgs[MOLD], 4)) {
  514.     if (log)
  515.         (void) fprintf(logfp, "%s Old %s %s\n", ptime(now), 
  516.                hosts[fd] ? hosts[fd] : "", bufp);
  517.     dprintf(stderr, "Old(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  518.     return reply(fd, 0);
  519.     } else if (STRN_EQ(cmdp, msgs[MHOST], 4)) {
  520.     if (hosts[fd])
  521.         free(hosts[fd]);
  522.     hosts[fd] = strdup(bufp);
  523.     if (log)
  524.         (void) fprintf(logfp, "%s Connect %s\n", ptime(now), hosts[fd]);
  525.     dprintf(stderr, "Connect(%d/%s)\n", fd, hosts[fd]);
  526.     return reply(fd, 0);
  527.     } else {
  528.     syslog(LOG_ERR, "Unknown command %s", cmdp);
  529.     if (log)
  530.         (void) fprintf(logfp, "%s Error %s unknown-cmd %s\n", ptime(now), 
  531.                hosts[fd], cmdp);
  532.     dprintf(stderr, "Error(%d/%s) unknown-cmd %s\n", fd, hosts[fd], cmdp);
  533.     return reply(fd, 0);
  534.     }
  535. #ifdef lint
  536.     /*NOTREACHED*/
  537.     return (0);
  538. #endif
  539. }
  540.  
  541. onpipe()
  542. {
  543.     register time_t now = time((long *)0);
  544.  
  545.     if (log)
  546.         fprintf(logfp, "%s Got SIGPIPE\n", ptime(now));
  547.     sigpiped++;
  548. }
  549.